package org.six11.olive3.net;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.six11.olive3.util.MiscFunctions;
import org.six11.util2.Debug;

public class Message {

  public String name;
  public List<Param> params;
  public List<Statement> statements;
  public String cachedParamString;

  private transient Map<String, String> paramCache;
  private transient String oliveString;

  public Message(String name, List<Param> params, List<Statement> statements) {
    this.name = name;
    this.params = params;
    this.statements = statements;
  }

  public Message(String name, List<Param> params) {
    this.name = name;
    this.params = params;
    this.statements = new ArrayList<Statement>();
  }
  
  public Message(String name) {
    this(name, new ArrayList<Param>());
  }
  
  public void setName(String newName) {
    name = newName;
    oliveString = null; // ensure we don't keep the old one around
  }
  
  public void addParam(String key, String value) {
    params.add(new Param(key, value));
  }

  public String getParamValue(String key) {
    String ret = null;
    if (paramCache == null) {
      paramCache = new HashMap<String, String>();
    }
    if (paramCache.containsKey(key)) {
      ret = paramCache.get(key);
    } else {
      for (Param p : params) {
        if (p.key.equals(key)) {
          ret = p.value;
          break;
        }
      }
      if (ret != null) {
        paramCache.put(key, ret);
      }
    }
    return ret;
  }
  
  /**
   * Returns the signature portion WITHOUT CURLY BRACES. For example, it could return:
   * 
   * chat_msg (userID="bob" msg="Hello there")
   */
  public String getSignature() {
    StringBuffer buf = new StringBuffer();
    buf.append(name + "(");
    for (int i = 0; i < params.size(); i++) {
      if (i > 0) {
        buf.append(" ");
      }
      buf.append(params.get(i).toOliveString());
    }
    buf.append(")");

    return buf.toString();
  }

  public String toOliveString() {
    String ret = null;
    if (oliveString != null) {
      ret = oliveString; // cache my olive string.
    } else {      
      StringBuffer buf = new StringBuffer();
      buf.append(getSignature());
      buf.append(" {");
      for (Statement s : statements) {
        buf.append("\n  " + s.toOliveString());
      }
      if (statements.size() > 0) {
        buf.append("\n}\n\n");
      } else {
        buf.append(" }\n\n");
      }
      ret = buf.toString();
      oliveString = ret;
    }
    return ret;
  }

  public static class Value {

    public String text;

    public Value(String text) {
      this.text = text;
    }

    public String toOliveString() {
      return text;
    }
  }

  public static class Statement {

    public String type;
    public List<Value> required;
    public List<Param> optional;

    public Statement(String type) {
      this.type = type;
      required = new ArrayList<Value>();
      optional = new ArrayList<Param>();
    }

    public Statement(String text, List<Value> required, List<Param> optional) {
      this.type = text;
      this.required = required;
      this.optional = optional;
    }

    public String toOliveString() {
      StringBuffer buf = new StringBuffer();
      buf.append(type);
      for (Value v : required) {
        buf.append(" " + v.toOliveString());
      }
      for (Param p : optional) {
        buf.append(" " + p.toOliveString());
      }
      return buf.toString();
    }
    
    public String getParamValue(String key) {
      String ret = null;
      for (Param p : optional) {
        if (p.key.equals(key)) {
          ret = p.value;
          break;
        }
      }
      return ret;
    }
  }

  public static class Param {

    String key, value;

    public Param(String key, String val) {
      this.key = key;
      if (val.startsWith("\"") && val.endsWith("\"")) {
        this.value = val.substring(1, val.length() - 1);
      } else {
        this.value = val;
      }
    }

    public String toOliveString() {
      return key + "=\"" + value + "\"";
    }
  }

  public static String createParamString(List<Param> pList) {
    StringBuffer buf = new StringBuffer();
    for (Param p : pList) {
      buf.append(p.toString() + " ");
    }
    return buf.toString().trim();
  }

  static void bug(String what) {
    Debug.out("Message", what);
  }

  /**
   * @return
   */
  public String getParamString() {
    if (cachedParamString != null) {
      return cachedParamString;
    } else {
      cachedParamString = createParamString(params);
      return cachedParamString;
    }
  }
  
  /**
   * @param stmt
   */
  public void addStatement(Statement stmt) {
    statements.add(stmt);
  }

  /**
   * Merges consecutive messages that have the same signature into one message.
   */
  public static List<Message> merge(List<Message> many) {
    List<Message> ret = new ArrayList<Message>();
    Message store = null;
    for (Message m : many) {
      if (store == null || !m.getSignature().equals(store.getSignature())) {
        store = m;
        ret.add(store);
      } else {
        store.statements.addAll(m.statements);
      }        
    }
    return ret;
  }
}
